home *** CD-ROM | disk | FTP | other *** search
/ CD Exchange / CD Exchange - Volume 1.iso / utils / disk / playcdda / play.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-21  |  24.6 KB  |  977 lines

  1. /* play.c: */
  2.  
  3. #include "includes.h"
  4.  
  5. #define VERSION "1.0 (21.11.93)"
  6.  
  7. #define CDDA_BUFSIZE 2368
  8. #define SUBCHANNEL_SIZE 16
  9. #define STD_BUFSIZE 2048
  10. #define SENSE_LENGTH 32
  11. #define AUDIO_BUFSIZE ((CDDA_BUFSIZE-SUBCHANNEL_SIZE)/g_compression_factor/4)
  12. #define TOTAL_BUFSIZE (g_buffers*(2*CDDA_BUFSIZE+2*AUDIO_BUFSIZE))
  13. #define CDDA_STD_BUFSIZE (STD_BUFSIZE+SENSE_LENGTH)
  14.  
  15. typedef short t_bool;
  16.  
  17. typedef struct toc {
  18.   char reserved1;
  19.   unsigned char flags;
  20.   unsigned char track_number;
  21.   char reserved2;
  22.   long address;
  23. } t_toc;
  24.  
  25. static char *TheVersion = "$VER: PlayCDDA " VERSION;
  26.  
  27. t_bool g_called_from_cli;
  28. char g_scsi_device[80];
  29. int g_scsi_id;
  30. UBYTE *g_buf_base = NULL;
  31. UBYTE *g_std_buf_base = NULL;
  32. UBYTE *g_cdda_buf[2];
  33. UBYTE *g_cdda_std_buf;
  34. UBYTE *g_sense_data;
  35. UBYTE *g_audio_buf[2];
  36. struct MsgPort *g_cdda_port = NULL;
  37. struct MsgPort *g_audio_port[2] = { NULL, NULL };
  38. ULONG g_cdda_sigmask;
  39. ULONG g_audio_sigmask[2];
  40. struct IOStdReq *g_scsireq = NULL;
  41. struct IOAudio *g_audioreq[2] = { NULL, NULL };
  42. struct SCSICmd *g_scsicmd = NULL;
  43. t_bool g_outstanding_cdda_request = FALSE;
  44. t_bool g_audio_device_open = FALSE;
  45. long g_period;
  46. int g_toc_length;
  47. t_toc g_toc[100];
  48. short g_volume = 1;
  49. /* possible values for g_compression_factor: 2, 3, 4, 6, 7, 12, 14, 28, 49 */
  50. unsigned short g_compression_factor = 2;
  51. unsigned short g_buffers = 4;
  52.  
  53. /* user interface variables: */
  54. struct Library *IconBase = NULL;
  55. struct Library *IntuitionBase = NULL;
  56. struct Library *GadToolsBase = NULL;
  57. struct GfxBase *GfxBase = NULL;
  58. struct Screen *g_screen = NULL;
  59. void *g_visual_info = NULL;
  60. struct Window *g_window = NULL;
  61. struct Gadget *g_glist = NULL;
  62. t_bool g_bye = FALSE;
  63. char g_track_str[3] = { 0, 0, 0 };
  64. char g_index_str[3] = { 0, 0, 0 };
  65. char g_time_str[6] = { 0, 0, ':', 0, 0, 0 };
  66. unsigned char g_track, g_index;
  67. unsigned char g_minute, g_seconds;
  68.  
  69. enum gadget_ids {
  70.   GID_SAMPLING_RATE = 21,
  71.   GID_BUFFERS,
  72.   GID_VOLUME,
  73.   GID_PREV,
  74.   GID_NEXT,
  75.   GID_START,
  76.   GID_STOP,
  77.   GID_TRACK,
  78.   GID_INDEX,
  79.   GID_TIME,
  80.  
  81.   /* always last: */
  82.   GID_MAX
  83. };
  84.  
  85. struct Gadget *g_gadgets[GID_MAX];
  86.  
  87. void Cleanup_User_Interface (void)
  88. {
  89.   if (g_window)
  90.     CloseWindow (g_window);
  91.   if (g_glist)
  92.     FreeGadgets (g_glist);
  93.   if (g_visual_info)
  94.     FreeVisualInfo (g_visual_info);
  95.   if (g_screen)
  96.     UnlockPubScreen (NULL, g_screen);
  97.   if (GfxBase)
  98.     CloseLibrary ((struct Library *) GfxBase);
  99.   if (GadToolsBase)
  100.     CloseLibrary (GadToolsBase);
  101.   if (IntuitionBase)
  102.     CloseLibrary (IntuitionBase);
  103. }
  104.  
  105. void Cleanup_Audio (void)
  106. {
  107.   if (g_buf_base) {
  108.     FreeMem (g_buf_base, TOTAL_BUFSIZE + 15);
  109.     g_buf_base = NULL;
  110.   }
  111.   if (g_audio_device_open) {
  112.     CloseDevice ((struct IORequest *) g_audioreq[0]);
  113.     g_audio_device_open = FALSE;
  114.   }
  115.   if (g_audio_port[0]) {
  116.     DeleteMsgPort (g_audio_port[0]);
  117.     g_audio_port[0] = NULL;
  118.   }
  119.   if (g_audio_port[1]) {
  120.     DeleteMsgPort (g_audio_port[1]);
  121.     g_audio_port[1] = NULL;
  122.   }
  123.   if (g_audioreq[0]) {
  124.     FreeMem (g_audioreq[0], sizeof (struct IOAudio));
  125.     g_audioreq[0] = NULL;
  126.   }
  127.   if (g_audioreq[1]) {
  128.     FreeMem (g_audioreq[1], sizeof (struct IOAudio));
  129.     g_audioreq[1] = NULL;
  130.   }
  131. }
  132.  
  133. void Cleanup (void)
  134. {
  135.   Cleanup_Audio ();
  136.  
  137.   if (g_std_buf_base)
  138.     FreeMem (g_std_buf_base, CDDA_STD_BUFSIZE + 15);
  139.   if (g_scsicmd)
  140.     FreeMem (g_scsicmd, sizeof (struct SCSICmd));
  141.   if (g_scsireq) {
  142.     if (g_scsireq->io_Device) {
  143.       if (g_outstanding_cdda_request) {
  144.         AbortIO ((struct IORequest *) g_scsireq);
  145.         WaitIO ((struct IORequest *) g_scsireq);
  146.       }
  147.       CloseDevice ((struct IORequest *) g_scsireq);
  148.     }
  149.     DeleteIORequest ((struct IORequest *) g_scsireq);
  150.   }
  151.   if (g_cdda_port)
  152.     DeleteMsgPort (g_cdda_port);
  153.  
  154.   Cleanup_User_Interface ();
  155.   
  156.   if (IconBase)
  157.     CloseLibrary (IconBase);
  158. }
  159.  
  160. void Fatal_Error (char *p_message, ...)
  161. {
  162.   va_list arg;
  163.  
  164.   static struct EasyStruct req = {
  165.     sizeof (struct EasyStruct),
  166.     0,
  167.     (UBYTE *) "PlayCDDA Error",
  168.     NULL,
  169.     (UBYTE *) "Abort"
  170.   };
  171.  
  172.   va_start (arg, p_message);
  173.   if (IntuitionBase) {
  174.     req.es_TextFormat = (UBYTE *) p_message;
  175.     EasyRequestArgs (NULL, &req, NULL, arg);
  176.   } else if (g_called_from_cli) {
  177.     VPrintf ((UBYTE *) p_message, (LONG *) arg);
  178.     WriteChars ((UBYTE *) "\n", 1);
  179.   } else
  180.     Alert (0x0000CDDA);
  181.  
  182.   va_end (p_message);
  183.  
  184.   exit (1);
  185. }
  186.  
  187. char *Open_User_Interface (void)
  188. {
  189.   static struct TextAttr Topaz8 = { (UBYTE *) "topaz.font", 8, 0, 0, };
  190.   struct TextFont *font;
  191.   int i, j;
  192.   static char *labels[20] = {
  193.     "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",
  194.     "12", "13", "14", "15", "16", "17", "18", "19", "20"
  195.   };
  196.   static char *sampling_rate_labels[] = {
  197.     "22050 bps",  /* 2 */
  198.     "14700 bps",  /* 3 */
  199.     "11025 bps",  /* 4 */
  200.     "7350 bps",   /* 6 */
  201.     "6300 bps",   /* 7 */
  202.     NULL
  203.   };
  204.   static char *buffers_labels[] = {
  205.     "2",
  206.     "4",
  207.     "8",
  208.     "16",
  209.     "32",
  210.     "64",
  211.     NULL
  212.   };
  213.   static char *volume_labels[] = {
  214.     "Low",
  215.     "Medium",
  216.     "High",
  217.     NULL
  218.   };
  219.   struct NewGadget ng;
  220.   struct Gadget *gad;
  221.   int topborder;
  222.  
  223.   if (!(IntuitionBase = OpenLibrary ((UBYTE *) "intuition.library", 37)))
  224.     return "cannot open intuition.library";
  225.   if (!(GadToolsBase = OpenLibrary ((UBYTE *) "gadtools.library", 37)))
  226.     return "cannot open gadtools.library";
  227.   if (!(GfxBase = (struct GfxBase *)
  228.       OpenLibrary ((UBYTE *) "graphics.library", 37)))
  229.     return "cannot open graphics.library";
  230.  
  231.   /* does the font exist? */
  232.   if (!(font = OpenFont (&Topaz8)))
  233.     return "cannot open topaz 8 font";
  234.   CloseFont (font);
  235.  
  236.   if (!(g_screen = LockPubScreen (NULL)))
  237.     return "cannot lock default public screen";
  238.  
  239.   if (!(g_visual_info = GetVisualInfo (g_screen, TAG_END)))
  240.     return "GetVisualInfo() failed";
  241.  
  242.   gad = CreateContext (&g_glist);
  243.  
  244.   topborder = g_screen->WBorTop + (g_screen->Font->ta_YSize + 1);
  245.  
  246.   ng.ng_Width = 20;
  247.   ng.ng_Height = 12;
  248.   ng.ng_TextAttr = &Topaz8;
  249.   ng.ng_VisualInfo = g_visual_info;
  250.   ng.ng_Flags = 0;
  251.  
  252.   for (i=0; i<5; i++)
  253.     for (j=0; j<4; j++) {
  254.       ng.ng_GadgetText = (UBYTE *) labels[i*4+j];
  255.       ng.ng_GadgetID = i*4 + j + 1;
  256.       ng.ng_LeftEdge = 10 + j * 24;
  257.       ng.ng_TopEdge = topborder + 2 + i * 16;
  258.       g_gadgets[ng.ng_GadgetID] = gad =
  259.         CreateGadget (BUTTON_KIND, gad, &ng,
  260.           GA_Disabled, TRUE,
  261.           TAG_END);
  262.     }
  263.  
  264.   ng.ng_GadgetID = GID_PREV;
  265.   ng.ng_GadgetText = (UBYTE *) "Prev";
  266.   ng.ng_Width = 44;
  267.   ng.ng_LeftEdge = 10;
  268.   ng.ng_TopEdge = topborder + 2 + 5 * 16;
  269.   g_gadgets[ng.ng_GadgetID] =
  270.     gad = CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
  271.   
  272.   ng.ng_GadgetID = GID_NEXT;
  273.   ng.ng_GadgetText = (UBYTE *) "Next";
  274.   ng.ng_LeftEdge = 58;
  275.   g_gadgets[ng.ng_GadgetID] =
  276.     gad = CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
  277.  
  278.   ng.ng_GadgetID = GID_START;
  279.   ng.ng_GadgetText = (UBYTE *) "Start";
  280.   ng.ng_LeftEdge = 120;
  281.   ng.ng_TopEdge = topborder + 2 + 4 * 16;
  282.   ng.ng_Width = 120;
  283.   ng.ng_Height = 28;
  284.   g_gadgets[ng.ng_GadgetID] =
  285.     gad = CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
  286.  
  287.   ng.ng_GadgetID = GID_STOP;
  288.   ng.ng_GadgetText = (UBYTE *) "Stop";
  289.   ng.ng_LeftEdge = 250;
  290.   g_gadgets[ng.ng_GadgetID] =
  291.     gad = CreateGadget (BUTTON_KIND, gad, &ng, TAG_END);
  292.  
  293.   ng.ng_GadgetText = (UBYTE *) "Sampling rate:";
  294.   ng.ng_GadgetID = GID_SAMPLING_RATE;
  295.   ng.ng_Width = 120;
  296.   ng.ng_Height = 12;
  297.   ng.ng_LeftEdge = 250;
  298.   ng.ng_TopEdge = topborder + 2;
  299.   g_gadgets[ng.ng_GadgetID] =
  300.     gad = CreateGadget (CYCLE_KIND, gad, &ng,
  301.       GTCY_Labels, sampling_rate_labels,
  302.       TAG_END);
  303.  
  304.   ng.ng_GadgetText = (UBYTE *) "Buffers:      ";
  305.   ng.ng_TopEdge += 16;
  306.   ng.ng_GadgetID = GID_BUFFERS;
  307.   g_gadgets[ng.ng_GadgetID] =
  308.     gad = CreateGadget (CYCLE_KIND, gad, &ng,
  309.       GTCY_Labels, buffers_labels,
  310.       GTCY_Active, 1,
  311.       TAG_END);
  312.  
  313.   ng.ng_GadgetText = (UBYTE *) "Volume:       ";
  314.   ng.ng_TopEdge += 16;
  315.   ng.ng_GadgetID = GID_VOLUME;
  316.   g_gadgets[ng.ng_GadgetID] =
  317.     gad = CreateGadget (CYCLE_KIND, gad, &ng,
  318.       GTCY_Labels, volume_labels,
  319.       TAG_END);
  320.  
  321.   ng.ng_GadgetID = GID_TRACK;
  322.   ng.ng_GadgetText = (UBYTE *) "Track";
  323.   ng.ng_Width = 25;
  324.   ng.ng_Height = 12;
  325.   ng.ng_LeftEdge = 170;
  326.   ng.ng_TopEdge = topborder + 2 + 3 * 16;
  327.   g_gadgets[ng.ng_GadgetID] =
  328.     gad = CreateGadget (TEXT_KIND, gad, &ng,
  329.       GTTX_Border, TRUE,
  330.       TAG_END);
  331.  
  332.   ng.ng_GadgetID = GID_INDEX;
  333.   ng.ng_GadgetText = (UBYTE *) "Index";
  334.   ng.ng_LeftEdge = 250;
  335.   g_gadgets[ng.ng_GadgetID] =
  336.     gad = CreateGadget (TEXT_KIND, gad, &ng,
  337.       GTTX_Border, TRUE,
  338.       TAG_END);
  339.   
  340.   ng.ng_GadgetID = GID_TIME;
  341.   ng.ng_GadgetText = (UBYTE *) "Time";
  342.   ng.ng_Width = 50;
  343.   ng.ng_LeftEdge = 320;
  344.   g_gadgets[ng.ng_GadgetID] =
  345.     gad = CreateGadget (TEXT_KIND, gad, &ng,
  346.       GTTX_Border, TRUE,
  347.       TAG_END);
  348.   
  349.  
  350.   if (!gad)
  351.     return "cannot create gadgets";
  352.  
  353.   g_window = OpenWindowTags (NULL,
  354.     WA_Title, TheVersion + 6,
  355.     WA_Gadgets, g_glist,
  356.     WA_Left, 20,
  357.     WA_Top, 20,
  358.     WA_Width, 385,
  359.     WA_Height, topborder + 98,
  360.     WA_IDCMP, IDCMP_CLOSEWINDOW | BUTTONIDCMP | CYCLEIDCMP,
  361.     WA_PubScreen, g_screen,
  362.     WA_DragBar, TRUE,
  363.     WA_DepthGadget, TRUE,
  364.     WA_CloseGadget, TRUE,
  365.     WA_Activate, TRUE,
  366.     WA_SmartRefresh, TRUE,
  367.     TAG_END);
  368.   if (!g_window)
  369.     return "cannot open window";
  370.  
  371.   return NULL;
  372. }
  373.  
  374. void Alloc_Audio (void)
  375. {
  376.   static UBYTE whichannel[] = { 1, 2, 4, 8 };
  377.   int i;
  378.  
  379.   /* allocate buffers: */
  380.  
  381.   g_buf_base = AllocMem (TOTAL_BUFSIZE + 15, MEMF_PUBLIC | MEMF_CHIP);
  382.   if (!g_buf_base)
  383.     Fatal_Error ("cannot allocate memory");
  384.  
  385.   /* make the buffer quad-word aligned. This greatly helps 
  386.    * performance on '040-powered systems with DMA SCSI
  387.    * controllers.
  388.    */
  389.  
  390.   g_cdda_buf[0] = (UBYTE *)(((long) g_buf_base + 15) & ~15);  
  391.   g_cdda_buf[1] = g_cdda_buf[0] + g_buffers * CDDA_BUFSIZE;
  392.   g_audio_buf[0] = g_cdda_buf[1] + g_buffers * CDDA_BUFSIZE;
  393.   g_audio_buf[1] = g_audio_buf[0] + g_buffers * AUDIO_BUFSIZE;
  394.  
  395.   /* allocate message ports and IO requests: */
  396.  
  397.   if (!(g_audio_port[0] = CreateMsgPort ()) || 
  398.       !(g_audio_port[1] = CreateMsgPort ()))
  399.     Fatal_Error ("cannot allocate message ports");
  400.  
  401.   g_audio_sigmask[0] = (1 << g_audio_port[0]->mp_SigBit);
  402.   g_audio_sigmask[1] = (1 << g_audio_port[1]->mp_SigBit);
  403.  
  404.   if (!(g_audioreq[0] = AllocMem (sizeof (struct IOAudio),
  405.                         MEMF_PUBLIC | MEMF_CLEAR)) ||
  406.       !(g_audioreq[1] = AllocMem (sizeof (struct IOAudio),
  407.                         MEMF_PUBLIC | MEMF_CLEAR)))
  408.     Fatal_Error ("cannot allocate memory");
  409.  
  410.   /* open audio device: */
  411.  
  412.   g_audioreq[0]->ioa_Request.io_Message.mn_ReplyPort = g_audio_port[0];
  413.   g_audioreq[0]->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
  414.   g_audioreq[0]->ioa_Request.io_Command = ADCMD_ALLOCATE;
  415.   g_audioreq[0]->ioa_Request.io_Flags = ADIOF_NOWAIT;
  416.   g_audioreq[0]->ioa_AllocKey = 0;
  417.   g_audioreq[0]->ioa_Data = whichannel;
  418.   g_audioreq[0]->ioa_Length = sizeof (whichannel);
  419.  
  420.   if (OpenDevice ((UBYTE *) AUDIONAME, 0,
  421.             (struct IORequest *) g_audioreq[0], 0))
  422.     Fatal_Error ("cannot open audio.device\n");
  423.  
  424.   g_audio_device_open = TRUE;
  425.  
  426.   *(g_audioreq[1]) = *(g_audioreq[0]);
  427.   g_audioreq[0]->ioa_Request.io_Message.mn_ReplyPort = g_audio_port[0];
  428.   g_audioreq[1]->ioa_Request.io_Message.mn_ReplyPort = g_audio_port[1];
  429.   g_audioreq[0]->ioa_Data = (UBYTE *) g_audio_buf[0];
  430.   g_audioreq[1]->ioa_Data = (UBYTE *) g_audio_buf[1];
  431.  
  432.   for (i=0; i<2; i++) {
  433.     struct IOAudio *req = g_audioreq[i];
  434.  
  435.     req->ioa_Request.io_Command = CMD_WRITE;
  436.     req->ioa_Request.io_Flags = ADIOF_PERVOL;
  437.     req->ioa_Length = g_buffers * AUDIO_BUFSIZE;
  438.     req->ioa_Period = g_period;
  439.     req->ioa_Volume = 64;
  440.     req->ioa_Cycles = 1;
  441.   }
  442. }
  443.  
  444. void Alloc_CDROM (void)
  445. {
  446.   g_std_buf_base = AllocMem (CDDA_STD_BUFSIZE + 15, MEMF_PUBLIC | MEMF_CHIP);
  447.   if (!g_std_buf_base)
  448.     Fatal_Error ("cannot allocate memory");
  449.  
  450.   g_scsicmd = AllocMem (sizeof (struct SCSICmd), MEMF_PUBLIC | MEMF_CHIP);
  451.   if (!g_scsicmd)
  452.     Fatal_Error ("cannot allocate memory");
  453.  
  454.   /* make the buffer quad-word aligned. This greatly helps 
  455.    * performance on '040-powered systems with DMA SCSI
  456.    * controllers.
  457.    */
  458.  
  459.   g_cdda_std_buf = (UBYTE *)(((long) g_std_buf_base + 15) & ~15);
  460.   g_sense_data = g_cdda_std_buf + STD_BUFSIZE;
  461.   
  462.   /* allocate message ports and IO requests: */
  463.  
  464.   if (!(g_cdda_port = CreateMsgPort ()))
  465.     Fatal_Error ("cannot allocate message port");
  466.  
  467.   g_cdda_sigmask = (1 << g_cdda_port->mp_SigBit);
  468.  
  469.   if (!(g_scsireq = CreateIORequest (g_cdda_port,
  470.                         sizeof (struct IOStdReq))))
  471.     Fatal_Error ("cannot create IO request\n");
  472.  
  473.   /* open SCSI device: */
  474.  
  475.   g_scsireq->io_Device = NULL;
  476.  
  477.   if (OpenDevice ((UBYTE *) g_scsi_device, g_scsi_id,
  478.             (struct IORequest *) g_scsireq, 0)) {
  479.     if (g_called_from_cli)
  480.       Fatal_Error ("Cannot open \"%s\", unit %ld",
  481.                  g_scsi_device, g_scsi_id);
  482.     else
  483.       Fatal_Error ("Cannot open \"%s\", unit %ld\n"
  484.                  "Please edit the tooltype entries\n"
  485.            "in the PlayCDDA icon!",
  486.                  g_scsi_device, g_scsi_id);
  487.   }
  488.  
  489. }
  490.  
  491. void Do_SCSI_Command (UBYTE *p_command, int p_length,
  492.              short p_phase, int p_sync)
  493. {
  494.   g_scsireq->io_Length   = sizeof (struct SCSICmd);
  495.   g_scsireq->io_Data     = (APTR) g_scsicmd;
  496.   g_scsireq->io_Command  = HD_SCSICMD;
  497.  
  498.   if (p_phase == -1) {
  499.     g_scsicmd->scsi_Data        = (UWORD *) g_cdda_std_buf;
  500.     g_scsicmd->scsi_Length      = STD_BUFSIZE;
  501.   } else {
  502.     g_scsicmd->scsi_Data        = (UWORD *) g_cdda_buf[p_phase];
  503.     g_scsicmd->scsi_Length      = g_buffers * CDDA_BUFSIZE;
  504.   }
  505.   g_scsicmd->scsi_Flags       = SCSIF_AUTOSENSE | SCSIF_READ;
  506.   g_scsicmd->scsi_SenseData   = (UBYTE *) g_sense_data;
  507.   g_scsicmd->scsi_SenseLength = SENSE_LENGTH;
  508.   g_scsicmd->scsi_SenseActual = 0;
  509.   g_scsicmd->scsi_Command     = (UBYTE *) p_command;
  510.   g_scsicmd->scsi_CmdLength   = p_length;
  511.  
  512.   if (p_sync) {
  513.     int i = 0;
  514.     
  515.     do {
  516.       DoIO ((struct IORequest *) g_scsireq);
  517.       if (g_scsicmd->scsi_Status == 0)
  518.         return;
  519.       i++;
  520.     } while (i < 2);
  521.     Fatal_Error ("sync SCSI command failed");
  522.   } else {
  523.     SendIO ((struct IORequest *) g_scsireq);
  524.     g_outstanding_cdda_request = TRUE;
  525.   }
  526. }
  527.  
  528. void Wait_CDROM_Command (void)
  529. {
  530.   ULONG sig;
  531.   
  532.   sig = Wait (SIGBREAKF_CTRL_C | g_cdda_sigmask);
  533.   if (sig & g_cdda_sigmask) {
  534.     if (CheckIO ((struct IORequest *) g_scsireq)) {
  535.       WaitIO ((struct IORequest *) g_scsireq);
  536.       g_outstanding_cdda_request = FALSE;
  537.       if (g_scsicmd->scsi_Status)
  538.         Fatal_Error ("async SCSI command failed");
  539.     }
  540.   }
  541.   if (sig & SIGBREAKF_CTRL_C)
  542.     exit (1);
  543. }
  544.  
  545. void Start_CDROM_Read (short p_phase, long p_sector)
  546. {
  547.   static UBYTE cmd[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  548.  
  549.   cmd[2] = (p_sector >> 24);
  550.   cmd[3] = ((p_sector >> 16) & 0xFF);
  551.   cmd[4] = ((p_sector >> 8) & 0xFF);
  552.   cmd[5] = (p_sector & 0xFF);
  553.   cmd[8] = g_buffers;
  554.   Do_SCSI_Command (cmd, 10, p_phase, FALSE);
  555. }
  556.  
  557. void Wait_CDROM_Read (void)
  558. {
  559.   Wait_CDROM_Command ();
  560. }
  561.  
  562. void Update_Track_Data (short p_num)
  563. {
  564.   g_track_str[0] = '0' + (p_num >> 4);
  565.   g_track_str[1] = '0' + (p_num & 15);
  566.   GT_SetGadgetAttrs (g_gadgets[GID_TRACK], g_window, NULL,
  567.              GTTX_Text, g_track_str, TAG_END);
  568.   g_track = p_num;
  569. }
  570.  
  571. void Update_Index_Data (short p_num)
  572. {
  573.   g_index_str[0] = '0' + (p_num >> 4);
  574.   g_index_str[1] = '0' + (p_num & 15);
  575.   GT_SetGadgetAttrs (g_gadgets[GID_INDEX], g_window, NULL,
  576.              GTTX_Text, g_index_str, TAG_END);
  577.   g_index = p_num;
  578. }
  579.  
  580. void Update_Time_Data (short p_min, short p_sec)
  581. {
  582.   g_time_str[0] = '0' + (p_min >> 4);
  583.   g_time_str[1] = '0' + (p_min & 15);
  584.   g_time_str[3] = '0' + (p_sec >> 4);
  585.   g_time_str[4] = '0' + (p_sec & 15);
  586.   GT_SetGadgetAttrs (g_gadgets[GID_TIME], g_window, NULL,
  587.              GTTX_Text, g_time_str, TAG_END);
  588.   g_minute = p_min;
  589.   g_seconds = p_sec;
  590. }
  591.  
  592. void Convert_Buffer (short p_phase)
  593. {
  594.   short i;
  595.   short sum;
  596.   BYTE *src = (BYTE *) g_cdda_buf[p_phase];
  597.   BYTE *dst = (BYTE *) g_audio_buf[p_phase];
  598.   BYTE *stop = src + (g_buffers * CDDA_BUFSIZE);
  599.   short skip = ((CDDA_BUFSIZE-SUBCHANNEL_SIZE)/4);
  600.   
  601.   while (src < stop) {
  602.     sum = 0;
  603.     for (i=0; i<g_compression_factor; i++) {
  604.       sum += src[1] + src[3];
  605.       src += 4;
  606.     }
  607.     for (i=g_volume-1; i--;)  /* this is faster than multiplication */
  608.       sum += sum;
  609.     sum /= (g_compression_factor << 1);
  610.     if (sum < -127)
  611.       sum = -127;
  612.     else if (sum > 127)
  613.       sum = 127;
  614.     *dst++ = sum;
  615.     skip -= g_compression_factor;
  616.     if (!skip) {
  617.       /* analyze Q-sub channel data: */
  618.       if (src[1] == 1) {
  619.         if (((UBYTE *) src)[2] != g_track)
  620.       Update_Track_Data (((UBYTE *) src)[2]);
  621.     if (((UBYTE *) src)[3] != g_index)
  622.       Update_Index_Data (((UBYTE *) src)[3]);
  623.     if (((UBYTE *) src)[5] != g_seconds ||
  624.         ((UBYTE *) src)[4] != g_minute)
  625.       Update_Time_Data (((UBYTE *) src)[4], ((UBYTE *) src)[5]);
  626.       }
  627.       /* skip Q-sub channel data: */
  628.       src += SUBCHANNEL_SIZE;
  629.       skip = ((CDDA_BUFSIZE-SUBCHANNEL_SIZE)/4);
  630.     }
  631.   }
  632. }
  633.  
  634. void Start_Play_Audio (short p_phase)
  635. {
  636.   BeginIO ((struct IORequest *) (g_audioreq[p_phase]));
  637. }
  638.  
  639. void Wait_Play_Audio (short p_phase)
  640. {
  641.   ULONG sig;
  642.  
  643.   sig = Wait (SIGBREAKF_CTRL_C | g_audio_sigmask[p_phase]);
  644.   if (sig & g_audio_sigmask[p_phase]) {
  645.     while (GetMsg (g_audio_port[p_phase]) == 0) ;
  646.   }
  647.   if (sig & SIGBREAKF_CTRL_C)
  648.     exit (1);
  649. }
  650.  
  651. #define FORMAT_CDDA 0x82
  652. #define FORMAT_STD 0x00
  653.  
  654. void Select_Block_Format (int p_format)
  655. {
  656.   static UBYTE cmd[6] = { 0x15, 0x10, 0, 0, 12, 0 };
  657.   static UBYTE mode[12] = { 0, 0, 0, 8,
  658.                 0, 0, 0, 0, 0, 0, 0, 0 };
  659.  
  660.   mode[4] = p_format;
  661.   switch (p_format) {
  662.   case FORMAT_CDDA:
  663.     mode[10] = (CDDA_BUFSIZE >> 8);
  664.     mode[11] = (CDDA_BUFSIZE & 0xFF);
  665.     break;
  666.   case FORMAT_STD:
  667.     mode[10] = (STD_BUFSIZE >> 8);
  668.     mode[11] = (STD_BUFSIZE & 0xFF);
  669.     break;
  670.   }
  671.  
  672.   memcpy (g_cdda_std_buf, mode, sizeof (mode));
  673.   Do_SCSI_Command (cmd, 6, -1, TRUE);
  674. }
  675.  
  676. void Read_TOC (void)
  677. {
  678.   static UBYTE cmd[10] = { 0x43, 0, 0, 0, 0, 0, 1,
  679.                  STD_BUFSIZE >> 8, STD_BUFSIZE & 0xFF,
  680.                0, };
  681.   UBYTE *buf = g_cdda_std_buf;
  682.  
  683.   Do_SCSI_Command (cmd, 10, -1, TRUE);
  684.   g_toc_length = ((buf[0] << 8) + buf[1] - 2) / 8;
  685.   memcpy (g_toc, buf + 4, 8 * g_toc_length);
  686. }
  687.  
  688. void Enable_Track_Buttons (void)
  689. {
  690.   int i;
  691.  
  692.   for (i=0; i<g_toc_length; i++) {
  693.     if (g_toc[i].track_number <= 20 &&
  694.         !(g_toc[i].flags & 4)) {
  695.       GT_SetGadgetAttrs (g_gadgets[g_toc[i].track_number],
  696.                    g_window, NULL,
  697.              GA_Disabled, FALSE,
  698.              TAG_END);
  699.     }
  700.   }
  701. }
  702.  
  703. long Track_Address (int p_track)
  704. {
  705.   int i;
  706.  
  707.   for (i=0; i<g_toc_length; i++) {
  708.     if (g_toc[i].track_number == p_track) {
  709.       if (g_toc[i].flags & 4)
  710.     return -1;
  711.       return g_toc[i].address;
  712.     }
  713.   }
  714.   return -1;
  715. }
  716.  
  717. int First_Track (void)
  718. {
  719.   int i;
  720.   
  721.   for (i=0; i<g_toc_length; i++) {
  722.     if (g_toc[i].track_number != 0xAA &&
  723.         !(g_toc[i].flags & 4))
  724.       return g_toc[i].track_number;
  725.   }
  726.   return 0;
  727. }
  728.  
  729. long Next_Track (int p_offset)
  730. {
  731.   int track = (g_track >> 4) * 10 + (g_track & 15);
  732.   long res;
  733.  
  734.   track += p_offset;
  735.   res = Track_Address (track);
  736.   return res == -1 ? Track_Address (track - p_offset) : res;
  737. }
  738.  
  739. void NTSC_or_PAL (void)
  740. {
  741.   if (GfxBase->DisplayFlags & PAL)
  742.     g_period = 3546895 / (44100 / g_compression_factor);
  743.   else
  744.     g_period = 3579545 / (44100 / g_compression_factor);
  745. }
  746.  
  747. void Init_Idle_Mode (void)
  748. {
  749.   GT_SetGadgetAttrs (g_gadgets[GID_STOP], g_window, NULL,
  750.              GA_Disabled, TRUE, TAG_END);
  751.   GT_SetGadgetAttrs (g_gadgets[GID_START], g_window, NULL,
  752.              GA_Disabled, FALSE, TAG_END);
  753.   GT_SetGadgetAttrs (g_gadgets[GID_SAMPLING_RATE], g_window, NULL,
  754.              GA_Disabled, FALSE, TAG_END);
  755.   GT_SetGadgetAttrs (g_gadgets[GID_BUFFERS], g_window, NULL,
  756.              GA_Disabled, FALSE, TAG_END);
  757.   GT_SetGadgetAttrs (g_gadgets[GID_PREV], g_window, NULL,
  758.              GA_Disabled, TRUE, TAG_END);
  759.   GT_SetGadgetAttrs (g_gadgets[GID_NEXT], g_window, NULL,
  760.              GA_Disabled, TRUE, TAG_END);
  761.   GT_SetGadgetAttrs (g_gadgets[GID_TRACK], g_window, NULL,
  762.                GTTX_Text, "", TAG_END);
  763.   GT_SetGadgetAttrs (g_gadgets[GID_INDEX], g_window, NULL,
  764.                GTTX_Text, "", TAG_END);
  765.   GT_SetGadgetAttrs (g_gadgets[GID_TIME], g_window, NULL,
  766.                GTTX_Text, "", TAG_END);
  767. }
  768.  
  769. void Init_Play_Mode (void)
  770. {
  771.   GT_SetGadgetAttrs (g_gadgets[GID_STOP], g_window, NULL,
  772.              GA_Disabled, FALSE, TAG_END);
  773.   GT_SetGadgetAttrs (g_gadgets[GID_START], g_window, NULL,
  774.              GA_Disabled, TRUE, TAG_END);
  775.   GT_SetGadgetAttrs (g_gadgets[GID_SAMPLING_RATE], g_window, NULL,
  776.              GA_Disabled, TRUE, TAG_END);
  777.   GT_SetGadgetAttrs (g_gadgets[GID_BUFFERS], g_window, NULL,
  778.              GA_Disabled, TRUE, TAG_END);
  779.   GT_SetGadgetAttrs (g_gadgets[GID_PREV], g_window, NULL,
  780.              GA_Disabled, FALSE, TAG_END);
  781.   GT_SetGadgetAttrs (g_gadgets[GID_NEXT], g_window, NULL,
  782.              GA_Disabled, FALSE, TAG_END);
  783.   g_track = g_index = g_minute = g_seconds = 0xFF;
  784. }
  785.  
  786. int Get_Intui_Message (long *p_sec_no)
  787. {
  788.   struct IntuiMessage *imsg;
  789.   struct Gadget *gad;
  790.   ULONG class;
  791.   UWORD code;
  792.  
  793.   if (imsg = GT_GetIMsg (g_window->UserPort)) {
  794.     class = imsg->Class;
  795.     code = imsg->Code;
  796.     gad = (struct Gadget *) imsg->IAddress;
  797.     GT_ReplyIMsg (imsg);
  798.     switch (class) {
  799.     case IDCMP_CLOSEWINDOW: {
  800.       g_bye = TRUE;
  801.       return TRUE;
  802.     }
  803.     case IDCMP_GADGETUP:
  804.       if (gad->GadgetID <= 20)
  805.         *p_sec_no = Track_Address (gad->GadgetID);
  806.       else if (gad->GadgetID == GID_STOP)
  807.         return TRUE;
  808.       else if (gad->GadgetID == GID_VOLUME)
  809.         g_volume = code + 1;
  810.       else if (gad->GadgetID == GID_PREV)
  811.         *p_sec_no = Next_Track (-1);
  812.       else if (gad->GadgetID == GID_NEXT)
  813.         *p_sec_no = Next_Track (+1);
  814.       break;
  815.     default:
  816.       break;
  817.     }
  818.   }
  819.   return FALSE;
  820. }
  821.  
  822. #define INC_SEC(ptr,inc) *(ptr) += (inc)
  823.  
  824. void main (int argc, char *argv[])
  825. {
  826.   short i=0;
  827.   long sec_no;
  828.   t_bool audio=FALSE;
  829.   char *err;
  830.   struct IntuiMessage *imsg;
  831.   t_bool done;
  832.   struct Gadget *gad;
  833.   ULONG class;
  834.   UWORD code;
  835.   t_bool tool_types_missing = FALSE;
  836.  
  837.   atexit (Cleanup);
  838.  
  839.   g_called_from_cli = (argc > 0);
  840.  
  841.   if (g_called_from_cli) {
  842.     if (argc != 3) {
  843.       Fatal_Error (
  844.         "usage: PlayCDDA scsi-device-name scsi-unit\n"
  845.         "e.g.:\n"
  846.         "  PlayCDDA scsi.device 1"
  847.         );
  848.     }
  849.     strcpy (g_scsi_device, argv[1]);
  850.     g_scsi_id = atoi (argv[2]);
  851.   } else {
  852.     char *str;
  853.     UBYTE **toolarray;
  854.     struct WBStartup *wbench_msg;
  855.     struct DiskObject *dobj;
  856.  
  857.     if (!(IconBase = OpenLibrary ((UBYTE *) "icon.library", 37)))
  858.       exit (1);
  859.     wbench_msg = (struct WBStartup *) argv;
  860.     dobj = GetDiskObject ((UBYTE *) wbench_msg->sm_ArgList->wa_Name);
  861.     if (!dobj)
  862.       exit (1);
  863.     toolarray = (UBYTE **) dobj->do_ToolTypes;
  864.     str = (char *) FindToolType (toolarray, (UBYTE *) "DEVICE");
  865.     if (!str)
  866.       tool_types_missing = TRUE;
  867.     strcpy (g_scsi_device, str);
  868.     str = (char *) FindToolType (toolarray, (UBYTE *) "UNIT");
  869.     if (!str)
  870.       tool_types_missing = TRUE;
  871.     g_scsi_id = atoi (str);
  872.     FreeDiskObject (dobj);
  873.   }
  874.  
  875.   err = Open_User_Interface ();
  876.   if (err)
  877.     Fatal_Error ("ERROR: %s", err);
  878.  
  879.   if (tool_types_missing)
  880.     Fatal_Error ("Tool type DEVICE and/or UNIT is missing");
  881.  
  882.   Alloc_CDROM ();  
  883.  
  884.   Select_Block_Format (FORMAT_CDDA);
  885.  
  886.   Read_TOC ();
  887.  
  888.   Enable_Track_Buttons ();
  889.  
  890.   if (!First_Track ())
  891.     Fatal_Error ("This is no audio disk");
  892.  
  893.   while (!g_bye) {
  894.  
  895.     Init_Idle_Mode ();
  896.  
  897.     done = FALSE;
  898.     while (!done) {
  899.       Wait (1 << g_window->UserPort->mp_SigBit);
  900.       while (imsg = GT_GetIMsg (g_window->UserPort)) {
  901.         class = imsg->Class;
  902.     code = imsg->Code;
  903.         gad = (struct Gadget *) imsg->IAddress;
  904.     GT_ReplyIMsg (imsg);
  905.         switch (class) {
  906.     case IDCMP_CLOSEWINDOW:
  907.           Select_Block_Format (FORMAT_STD);  
  908.       exit (0);
  909.         case IDCMP_GADGETUP:
  910.       if (gad->GadgetID <= 20) {
  911.             sec_no = Track_Address (gad->GadgetID);
  912.         done = TRUE;
  913.       } else if (gad->GadgetID == GID_START) {
  914.         sec_no = Track_Address (First_Track ());
  915.         done = TRUE;
  916.       } else if (gad->GadgetID == GID_VOLUME)
  917.             g_volume = code + 1;
  918.       else if (gad->GadgetID == GID_SAMPLING_RATE) {
  919.         static short factors[] = { 2, 3, 4, 6, 7 };
  920.         g_compression_factor = factors[code];
  921.       } else if (gad->GadgetID == GID_BUFFERS) {
  922.         static short buffers[] = { 2, 4, 8, 16, 32, 64 };
  923.         g_buffers = buffers[code];
  924.       }
  925.           break;
  926.         default:
  927.           break;
  928.         }
  929.       }
  930.     }
  931.  
  932.     Init_Play_Mode ();
  933.  
  934.     NTSC_or_PAL ();
  935.     Alloc_Audio ();
  936.  
  937.     Start_CDROM_Read (0, sec_no);
  938.     Wait_CDROM_Read ();
  939.     INC_SEC (&sec_no, g_buffers);
  940.     Start_CDROM_Read (1, sec_no);
  941.     Convert_Buffer (0);
  942.     Wait_CDROM_Read ();
  943.     Convert_Buffer (1);
  944.     Start_Play_Audio (0);
  945.     Start_Play_Audio (1);
  946.     INC_SEC (&sec_no, g_buffers);
  947.     Start_CDROM_Read (0, sec_no);
  948.  
  949.     for (;;) {
  950.       /* HERE: scsi-request i is active,
  951.                play-audio i is active and
  952.            play-audio 1-i is queued */
  953.  
  954.       if (Get_Intui_Message (&sec_no))
  955.         break;
  956.       Wait_CDROM_Read ();
  957.       INC_SEC (&sec_no, g_buffers);
  958.       Start_CDROM_Read (1-i, sec_no);
  959.       Wait_Play_Audio (i);
  960.       Convert_Buffer (i);
  961.       Start_Play_Audio (i);
  962.       i = 1-i;
  963.     }
  964.     
  965.     Wait_CDROM_Read ();
  966.     Wait_Play_Audio (i);
  967.     Wait_Play_Audio (1-i);
  968.     
  969.     Cleanup_Audio ();
  970.   }
  971.  
  972.   Select_Block_Format (FORMAT_STD);
  973.  
  974.   exit (0);
  975. }
  976.  
  977.